using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Konst3d.Efficiency
{
    public class ObservableDictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue>
    {
        #region Types

        public delegate void ItemCallback(KeyValuePair<TKey, TValue> item);
        public delegate void ItemReplacedCallback(TKey key, TValue oldValue, TValue newValue);
        public delegate void ClearCallback();

        #endregion // Types

        #region Events

        public event ItemCallback ItemAdded;
        public event ItemCallback ItemRemoved;
        public event ItemReplacedCallback ItemReplaced;
        public event ClearCallback Cleaned;

        #endregion // Events

        #region ICollection<KeyValuePair<TKey,TValue>> Members

        public void Add(KeyValuePair<TKey, TValue> item)
        {
            _dictionary.Add(item.Key, item.Value);

            if (ItemAdded != null)
            {
                ItemAdded(item);
            }
        }

        public void Clear()
        {
            _dictionary.Clear();

            if (Cleaned != null)
            {
                Cleaned();
            }
        }

        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            return _dictionary.Contains(item);
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            (_dictionary as ICollection<KeyValuePair<TKey, TValue>>).CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get
            {
                return _dictionary.Count;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                return (_dictionary as ICollection<KeyValuePair<TKey, TValue>>).IsReadOnly;
            }
        }

        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            bool removed = false;

            if (_dictionary.ContainsKey(item.Key))
            {
                KeyValuePair<TKey, TValue> pair = default(KeyValuePair<TKey, TValue>);

                if (ItemRemoved != null)
                {
                    pair = new KeyValuePair<TKey, TValue>(item.Key, _dictionary[item.Key]);
                }

                removed = _dictionary.Remove(item.Key);

                if (ItemRemoved != null && removed)
                {
                    ItemRemoved(pair);
                }
            }

            return removed;
        }

        #endregion

        #region IEnumerable<KeyValuePair<TKey,TValue>> Members

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return _dictionary.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return (_dictionary as System.Collections.IEnumerable).GetEnumerator();
        }

        #endregion

        #region IDictionary<TKey,TValue> Members

        public void Add(TKey key, TValue value)
        {
            _dictionary.Add(key, value);

            if (ItemAdded != null)
            {
                ItemAdded(new KeyValuePair<TKey, TValue>(key, value));
            }
        }

        public bool ContainsKey(TKey key)
        {
            return _dictionary.ContainsKey(key);
        }

        public ICollection<TKey> Keys
        {
            get
            {
                return _dictionary.Keys;
            }
        }

        public bool Remove(TKey key)
        {
            bool removed = false;

            if (_dictionary.ContainsKey(key))
            {
                KeyValuePair<TKey, TValue> pair = default(KeyValuePair<TKey, TValue>);

                if (ItemRemoved != null)
                {
                    pair = new KeyValuePair<TKey, TValue>(key, _dictionary[key]);
                }

                removed = _dictionary.Remove(key);

                if (ItemRemoved != null && removed)
                {
                    ItemRemoved(pair);
                }
            }

            return removed;
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            return _dictionary.TryGetValue(key, out value);
        }

        public ICollection<TValue> Values
        {
            get
            {
                return _dictionary.Values;
            }
        }

        public TValue this[TKey key]
        {
            get
            {
                return _dictionary[key];
            }
            set
            {
                bool contains = _dictionary.ContainsKey(key);
                TValue old = default(TValue);

                if (contains && ItemReplaced != null)
                {
                    old = _dictionary[key];
                }

                _dictionary[key] = value;

                if (contains && ItemReplaced != null)
                {
                    ItemReplaced(key, old, value);
                }
                else if (ItemAdded != null)
                {
                    ItemAdded(new KeyValuePair<TKey, TValue>(key, value));
                }
            }
        }

        #endregion

        #region Fields

        private Dictionary<TKey, TValue> _dictionary = new Dictionary<TKey,TValue>();

        #endregion // Fields
    }
}
